package com.example.demo.demos.broadcast.socket;

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

public class Client {
    private Socket socket;
    private PrintWriter pw;

    // 存储最后响应的MD5哈希值（初始为null）
    private String lastMD5 = null;

    // 添加UDP监听线程的引用
    private UDPListener udpListener;

    public Client() {
        boolean connected = false;

        while (!connected) {
            try {
                System.out.println("正在连接服务端...");
                socket = new Socket("192.168.1.154", 8088);
                System.out.println("服务端连接成功!");
                connected = true;

                // 创建并启动UDP监听线程
                udpListener = new UDPListener();
                new Thread(udpListener).start();
            } catch (IOException e) {
                System.out.println("连接失败，5秒后重试...");
                try {
                    Thread.sleep(5000); // 等待5秒后重试
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    System.out.println("连接中断");
                    break;
                }
            }
        }
    }

    // 计算字节数组的MD5哈希值
    private String calculateMD5(byte[] data) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(data);
            StringBuilder hexString = new StringBuilder();
            for (byte b : digest) {
                hexString.append(String.format("%02x", b));
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5算法不可用", e);
        }
    }

    public void start() {
        if (socket == null || socket.isClosed()) {
            System.out.println("无法启动，未连接到服务器");
            return;
        }

        try {
            OutputStream out = socket.getOutputStream();
            OutputStreamWriter osw = new OutputStreamWriter(out, StandardCharsets.UTF_8);
            BufferedWriter bw = new BufferedWriter(osw);
            pw = new PrintWriter(bw, true);

            Scanner sc = new Scanner(System.in);
            new Thread(new ServerHandler()).start();

            while (true) {
                try {
                    String input = sc.nextLine();
                    if ("exit".equals(input)) {
                        pw.println(input);
                        break;
                    }
                    pw.println(input);
                } catch (Exception e) {
                    System.out.println("发送消息失败: " + e.getMessage());
                    break;
                }
            }
        } catch (IOException e) {
            System.out.println("启动失败: " + e.getMessage());
        } finally {
            // 关闭UDP监听线程
            if (udpListener != null) {
                udpListener.close();
            }
            try {
                if (socket != null && !socket.isClosed()) {
                    socket.close();
                }
            } catch (Exception e) {
                System.out.println("关闭连接时出错: " + e.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        Client client = new Client();
        client.start();
    }

    // UDP广播监听器
    private class UDPListener implements Runnable {
        private volatile boolean running = true;
        private DatagramSocket udpSocket;

        @Override
        public void run() {
            if (socket == null || socket.isClosed()) {
                System.out.println("UDP监听未启动，无可用连接");
                return;
            }

            try {
                udpSocket = new DatagramSocket(8089);
                udpSocket.setBroadcast(true);
                byte[] buffer = new byte[1024];
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

                while (running) {
                    try {
                        udpSocket.receive(packet);
                        byte[] receivedData = Arrays.copyOf(packet.getData(), packet.getLength());

                        try {
                            // 反序列化对象
                            ByteArrayInputStream bis = new ByteArrayInputStream(receivedData);
                            ObjectInputStream ois = new ObjectInputStream(bis);
                            Object obj = ois.readObject();

                            // 将对象转为字符串
                            String objString = obj.toString();
                            System.out.println("收到广播对象: " + objString);

                            // 计算原始字节数据的MD5
                            String currentMD5 = calculateMD5(receivedData);

                            // 检查是否与上次响应的MD5相同
                            if (lastMD5 == null) {
                                // 第一次接收消息，初始化MD5值并响应
                                lastMD5 = currentMD5;
                                sendResponse(currentMD5);
                                System.out.println("首次收到广播，发送响应");
                            } else if (!lastMD5.equals(currentMD5)) {
                                // MD5值不同，更新并响应
                                lastMD5 = currentMD5;
                                sendResponse(currentMD5);
                                System.out.println("收到新广播，更新MD5并响应");
                            } else {
                                // MD5值相同，不响应
                                System.out.println("收到重复广播，MD5相同不响应");
                            }
                        } catch (ClassNotFoundException | InvalidClassException e) {
                            System.out.println("无法反序列化对象: " + e.getMessage());
                        } catch (IOException e) {
                            System.out.println("反序列化失败: " + e.getMessage());
                        }
                    } catch (SocketException e) {
                        if ("socket closed".equals(e.getMessage())) {
                            System.out.println("UDP监听已关闭");
                            break;
                        }
                    } catch (Exception e) {
                        if (running) {
                            System.out.println("UDP接收异常: " + e.getMessage());
                        }
                    }
                }
            } catch (Exception e) {
                System.out.println("UDP监听启动失败: " + e.getMessage());
            } finally {
                close();
            }
        }

        // 发送响应给服务端（只发送MD5值）
        private void sendResponse(String md5) {
            // 检查连接是否有效
            if (pw != null && !pw.checkError() && socket != null && !socket.isClosed()) {
                String response = "BROADCAST_MD5:" + md5;
                pw.println(response);
                System.out.println("已发送MD5回应给服务端: " + md5);
            } else {
                System.out.println("无法发送回应: 连接已断开");
            }
        }

        public void close() {
            running = false;
            if (udpSocket != null && !udpSocket.isClosed()) {
                udpSocket.close();
            }
        }
    }

    class ServerHandler implements Runnable {
        public void run() {
            if (socket == null || socket.isClosed()) {
                System.out.println("服务器消息处理未启动，无可用连接");
                return;
            }

            try {
                InputStream is = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
                BufferedReader br = new BufferedReader(isr);

                String serverMsg;
                while ((serverMsg = br.readLine()) != null) {
                    System.out.println(serverMsg);
                }
                System.out.println("服务器连接已关闭");
            } catch (SocketException e) {
                System.out.println("与服务器的连接已断开");
            } catch (IOException e) {
                System.out.println("读取服务器消息失败: " + e.getMessage());
            } finally {
                try {
                    if (socket != null && !socket.isClosed()) {
                        socket.close();
                    }
                } catch (IOException e) {
                    System.out.println("关闭连接时出错: " + e.getMessage());
                }
            }
        }
    }
}